

def generateBalanceW(ann, layer, index):
    """
    Get value of increment synapsis
    """
    #For each layer
    inc_row = []
    inc_matrix = []
    inc = ann.net.inc_synapses[index]
    row_size = len(ann.net.synapses[index].matrix)
    col_size = len(layer.gradient)
    for row in range(0, row_size):
    #     inc.matrix.append([])
        for colGrad in range(0, col_size):
            temp = ann.net.alpha*layer.inputs[row]*layer.gradient[colGrad]
            inc_row.append(temp)
            #inc.matrix[len(inc.matrix)-1].append(temp)
        inc_matrix.append(inc_row)
        inc_row = []
        #print inc_matrix
    inc.matrix = inc_matrix
    #print "inc_matrix index "+str(index)
    #print inc.matrix

    
def getError(self, d, s):
    pass
    
def backpropagation(ann,  expectedOutput):
    """
    Main function to perform a backpropagation algorithm. 
    """
    #print "Num of layers: "+str(len(self.layers))
    ann.currentLayer = "outputLayer"
    generateGradient(ann.net.layers[ann.currentLayer], expectedOutput)
    #print "Backpropagation of layer: "+str(len(self.layers)-1)
    generateBalanceW(ann, ann.net.layers[ann.currentLayer], ann.numLayers - 1)
    for numHidden in range(ann.numLayers - 2, -1, -1):
        if numHidden == (ann.numLayers - 2):
            #print "Backpropagation of layer: "+str(numHidden)
            ann.currentLayer = "hiddenLayer"+str(numHidden)
            generateV(ann.net.layers[ann.currentLayer],  ann.net.layers["outputLayer"], ann.net.synapses[numHidden+1])
            generateGradient(ann.net.layers[ann.currentLayer])
            generateBalanceW(ann, ann.net.layers[ann.currentLayer], numHidden)
        else:
            if numHidden == 0:
                #First layer, input Layer
                #Verify that there is more than 3 layers or just 3
                #print "Backpropagation of layer: "+str(numHidden)
                ann.currentLayer = "inputLayer"
                generateV(ann.net.layers[ann.currentLayer],  ann.net.layers["hiddenLayer1"], ann.net.synapses[numHidden+1])
                generateGradient(ann.net.layers[ann.currentLayer])  
                generateBalanceW(ann,  ann.net.layers[ann.currentLayer], numHidden)
            else:
                #Rest of layers (hiddenLayers)
                #print "Backpropagation of layer: "+str(numHidden)
                ann.currentLayer = "hiddenLayer"+str(numHidden)
                generateV(ann.net.layers[ann.currentLayer], ann.net.layers["hiddenLayer"+str(numHidden+1)], ann.net.synapses[numHidden+1])
                generateGradient(ann.net.layers[ann.currentLayer])  
                generateBalanceW(ann,  ann.net.layers[ann.currentLayer], numHidden)
             
    
def adjustSynapsis(ann):
    """
    For each synapsis, adjust values
    """
    for syn in range(0, len(ann.net.synapses)):
        adjustValues(ann.net.synapses[syn],  ann.net.inc_synapses[syn])

def generateGradient(layer,  expectedOutput=[]):
    """
    Generate gradient from the actual layer. Depend from expected ouput. 
    If layer is the last layer, expectedOuput would be the real expected ouput, 
    if not, this parameter will be 'v', an expected output from the current layer. 
    """
    param = []
    if not expectedOutput:
        param = layer.v
    else:
        param = expectedOutput
    layer.gradient = []
    for index in range(0, len(param)):
        d = param[index]
        s = layer.values[index]
        if not expectedOutput:
            layer.gradient.append((d)*s*(1-s))
        else:
            layer.gradient.append((d-s)*s*(1-s))

def generateV(layer, nextLayer, nextSynapsis):
    """
    Generate expected ouput for the current layer 
    """
    for row in range(1, len(nextLayer.inputs)):
        v_temp = 0.0
        for col in range(0, len(nextSynapsis.matrix[row])):
            v_temp += nextSynapsis.matrix[row][col]*nextLayer.gradient[col]
        if len(layer.v) < (len(nextLayer.inputs) -1):
            layer.v.append(v_temp)
        else:
            layer.v[row-1] = v_temp
                                
def adjustValues(syn, increment):
    """
    Adjust synapsis values, a matrix level
    """
    for row in range(0, len(syn.matrix)):
        for col in range(0, len(syn.matrix[row])):
            syn.matrix[row][col] = syn.matrix[row][col] + increment.matrix[row][col]
            

